package GUI;

import org.cloudbus.cloudsim.*;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.provisioners.BwProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.PeProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.RamProvisionerSimple;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class SAS {
    public static int vmNum = 5;  // 设置虚拟机数量
    public static int cloudletNum = 10;  // 设置云任务数量
    private static List<Cloudlet> cloudletList;  // 云任务列表
    private static List<Vm> vmList;  // 虚拟机列表

    private static double initialTemperature = 1000.0;  // 初始温度
    private static double coolingRate = 0.995;  // 冷却速率
    private static double temperature = initialTemperature;  // 当前温度
    private static Random random = new Random();  // 随机数生成器

    public static void main(String[] args) {

        try {
            int num_user = 1;  // 设置用户数量
            Calendar calendar = Calendar.getInstance();  // 创建日历对象
            boolean trace_flag = false;  // 设置跟踪标志
            CloudSim.init(num_user, calendar, trace_flag);  // 初始化CloudSim

            Datacenter datacenter0 = createDatacenter("Datacenter_0");  // 创建数据中心

            DatacenterBroker broker = createBroker();  // 创建经纪人
            int brokerId = broker.getId();  // 获取经纪人ID

            int vmid = 0;
            int[] mipss = new int[]{1000, 800, 600, 400, 300};  // 设置每个虚拟机的MIPS值
            long size = 10000;  // 虚拟机磁盘大小
            int ram = 2048;  // 虚拟机RAM大小
            long bw = 1000;  // 虚拟机带宽
            int pesNumber = 1;  // 虚拟机使用的处理器数量
            String vmm = "Xen";  // 虚拟机监控器类型

            vmList = new ArrayList<>();
            for (int i = 0; i < vmNum; i++) {
                // 创建虚拟机并添加到虚拟机列表
                vmList.add(new Vm(vmid, brokerId, mipss[i], pesNumber, ram, bw, size, vmm, new CloudletSchedulerSpaceShared()));
                vmid++;
            }

            broker.submitVmList(vmList);  // 提交虚拟机列表给经纪人

            int id = 0;
            long[] lengths = new long[]{10000, 25000, 15000, 30000, 20000, 18000, 21000, 23000, 27000, 24000};  // 设置云任务长度
            long fileSize = 500;  // 设置云任务文件大小
            long outputSize = 1000;  // 设置云任务输出大小
            UtilizationModel utilizationModel = new UtilizationModelFull();  // 设置资源利用率模型

            cloudletList = new ArrayList<>();
            for (int i = 0; i < cloudletNum; i++) {
                // 创建云任务并添加到云任务列表
                Cloudlet cloudlet = new Cloudlet(id, lengths[i], pesNumber, fileSize, outputSize,
                        utilizationModel, utilizationModel, utilizationModel);
                cloudlet.setUserId(brokerId);  // 设置云任务用户ID
                cloudletList.add(cloudlet);
                id++;
            }

            broker.submitCloudletList(cloudletList);  // 提交云任务列表给经纪人

            simulatedAnnealingScheduling(broker);  // 执行模拟退火调度

            CloudSim.startSimulation();  // 启动模拟

            List<Cloudlet> newList = broker.getCloudletReceivedList();  // 获取执行结果
            CloudSim.stopSimulation();  // 停止模拟
            printCloudletList_1(newList);  // 打印执行结果
            Log.printLine("模拟退火调度完成！");  // 输出调度完成信息
        } catch (Exception e) {
            e.printStackTrace();
            Log.printLine("参数错误");  // 错误处理
        }
    }

    // 模拟退火调度函数
    private static void simulatedAnnealingScheduling(DatacenterBroker broker) {
        List<Cloudlet> initialCloudlets = new ArrayList<>(cloudletList);
        List<Vm> initialVms = new ArrayList<>(vmList);

        double currentCost = calculateCost(initialCloudlets);  // 计算当前调度的成本

        List<Cloudlet> bestCloudlets = new ArrayList<>(initialCloudlets);
        double bestCost = currentCost;

        while (temperature > 1) {
            List<Cloudlet> newCloudlets = new ArrayList<>(initialCloudlets);
            perturbSolution(newCloudlets);  // 扰动当前解

            double newCost = calculateCost(newCloudlets);  // 计算新解的成本

            // 如果新解更优，或者以一定概率接受更差的解（模拟退火机制）
            if (newCost < currentCost || random.nextDouble() < Math.exp((currentCost - newCost) / temperature)) {
                currentCost = newCost;
                initialCloudlets = new ArrayList<>(newCloudlets);
            }

            // 如果新解比当前解好，则更新最佳解
            if (newCost < bestCost) {
                bestCost = newCost;
                bestCloudlets = new ArrayList<>(newCloudlets);
            }

            temperature *= coolingRate;  // 降低温度
        }

        broker.submitCloudletList(bestCloudlets);  // 提交最佳云任务列表给经纪人
    }

    // 扰动当前解（交换两个云任务的虚拟机分配）
    private static void perturbSolution(List<Cloudlet> cloudlets) {
        int index1 = random.nextInt(cloudlets.size());
        int index2 = random.nextInt(cloudlets.size());
        while (index1 == index2) {
            index2 = random.nextInt(cloudlets.size());
        }

        Cloudlet cloudlet1 = cloudlets.get(index1);
        Cloudlet cloudlet2 = cloudlets.get(index2);
        int tempVmId = cloudlet1.getVmId();
        cloudlet1.setVmId(cloudlet2.getVmId());
        cloudlet2.setVmId(tempVmId);
    }

    // 计算调度的总成本
    private static double calculateCost(List<Cloudlet> cloudlets) {
        double totalTime = 0.0;
        for (Cloudlet cloudlet : cloudlets) {
            totalTime += cloudlet.getFinishTime();  // 累加每个云任务的完成时间
        }
        return totalTime;
    }

    // 创建数据中心
    private static Datacenter createDatacenter(String name) {
        List<Host> hostList = new ArrayList<>();
        int MIPS = 1000;  // 每个处理器的MIPS值
        int hostId = 0;
        int ram = 2048;  // 主机的内存
        long storage = 1000000;  // 主机的存储
        int bw = 10000;  // 主机的带宽
        for (int i = 0; i < vmNum; i++) {
            List<Pe> peList = new ArrayList<>();
            peList.add(new Pe(0, new PeProvisionerSimple(MIPS)));
            hostList.add(new Host(hostId, new RamProvisionerSimple(ram),
                    new BwProvisionerSimple(bw), storage, peList, new VmSchedulerTimeShared(peList)));
            hostId++;
        }

        String arch = "x86";
        String os = "Linux";
        String vmm = "Xen";
        double time_zone = 10.0;
        double cost = 3.0;
        double costPerMem = 0.05;
        double costPerStorage = 0.001;
        double costPerBw = 0.0;
        LinkedList<Storage> storageList = new LinkedList<>();
        DatacenterCharacteristics characteristics = new DatacenterCharacteristics(arch, os, vmm, hostList, time_zone, cost, costPerMem, costPerStorage, costPerBw);

        Datacenter datacenter = null;
        try {
            datacenter = new Datacenter(name, characteristics, new VmAllocationPolicySimple(hostList), storageList, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return datacenter;
    }

    // 创建经纪人
    private static DatacenterBroker createBroker() {
        DatacenterBroker broker = null;
        try {
            broker = new DatacenterBroker("Broker");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return broker;
    }

    // 打印云任务执行结果
    private static String printCloudletList_1(List<Cloudlet> list) {
        int size = list.size();
        Cloudlet cloudlet;
        String indent = "    ";
        Log.printLine();
        Log.printLine("========== OUTPUT ==========");
        Log.printLine("No." + indent + "Cloudlet ID" + indent + "STATUS" + indent
                + "Data center ID" + indent + "VM ID" + indent + "VM mips" + indent + "Cloudlet Length" + indent + "Time"
                + indent + "Start Time" + indent + "Finish Time");

        DecimalFormat dft = new DecimalFormat("###.##");
        for (int i = 0; i < size; i++) {
            cloudlet = list.get(i);
            Log.print(i + 1 + indent + indent + cloudlet.getCloudletId() + indent + indent);

            if (cloudlet.getStatus() == Cloudlet.SUCCESS) {
                Log.print("SUCCESS");

                Log.printLine(indent + indent + indent + cloudlet.getResourceId()
                        + indent + indent + indent + cloudlet.getVmId()
                        + indent + indent + getVmById(cloudlet.getVmId()).getMips()
                        + indent + indent + cloudlet.getCloudletLength()
                        + indent + indent + indent + indent
                        + dft.format(cloudlet.getActualCPUTime()) + indent
                        + indent + dft.format(cloudlet.getExecStartTime())
                        + indent + indent
                        + dft.format(cloudlet.getFinishTime()));
            }
        }
        return dft.format(list.get(size - 1).getFinishTime());
    }

    // 根据虚拟机ID获取虚拟机对象
    public static Vm getVmById(int vmId) {
        for (Vm v : vmList) {
            if (v.getId() == vmId)
                return v;
        }
        return null;
    }
}
